package es.upm.gib.mappingAPI2.RDF2NL;

import es.upm.gib.mappingAPI2.*;
import es.upm.gib.owlbasicmodel2.OWLBasicModel2;

import java.util.ArrayList;
import java.util.List;

/**
 * todo doc
 */
public class RDF2NL {

    public static List<NLText> viewToNL(View view, OWLBasicModel2 model) {
        List<NLText> nlTextList = new ArrayList<NLText>();
        for (Path path : view.getPaths()) {
            nlTextList.add(pathToNL(path, model));
        }
        addIdentifiers(view, model, nlTextList);
        return nlTextList;
    }

    private static void addIdentifiers(View view, OWLBasicModel2 model, List<NLText> nlTextList) {
        for (Identifier identifier : view.getIdentifiers()) {
            addIdentifier(view.getPath(identifier.getPath()).getClass(identifier.getClass_()), identifier.getClass_(), model, nlTextList.get(identifier.getPath()));
        }
    }

    private static void addIdentifier(String class_, int position, OWLBasicModel2 model, NLText nlText) {
        nlText.addTextElement(new NLText.AnnotatedText(". The ", NLText.Format.PLAIN));
        nlText.addTextElement(new NLText.AnnotatedText(addPlural(getLabel(class_, model)), NLText.Format.CLASS, position));
        nlText.addTextElement(new NLText.AnnotatedText(" act as identifier", NLText.Format.PLAIN));
    }

    public static NLText pathToNL(Path path, OWLBasicModel2 model) {
        NLText nlText = new NLText();
        if (!path.getPath().isEmpty()) {
            // convert first triple
            convertFirstTriple(path.getPath().get(0), model, nlText);
            for (int i = 1; i < path.getPath().size(); i++) {
                convertMiddleTriple(path.getPath().get(i), i, model, nlText);
            }
            addRestrictions(path, model, nlText);
        }
        return nlText;
    }

    private static void convertFirstTriple(Triple triple, OWLBasicModel2 model, NLText nlText) {
        nlText.addTextElement(new NLText.AnnotatedText(addPlural(getLabel(triple.getOrigin(), model)), NLText.Format.CLASS, getOriginPosition(0)));
        nlText.addTextElement(new NLText.AnnotatedText(" that ", NLText.Format.PLAIN));
        convertPropertyAndDestination(triple, 0, model, nlText);
    }

    private static String getLabel(String name, OWLBasicModel2 model) {
        String label;
        if (model.isRDFClass(name)) {
            label = model.getLabelOfClass(name);
        } else if (model.isObjectProperty(name)) {
            label = model.getLabelOfObjectProperty(name);
        } else if (model.isDataTypeProperty(name)) {
            label = model.getLabelOfDataProperty(name);
        } else {
            label = name;
        }
        return shortenName(label);
    }

    private static String shortenName(String name) {
        if (name.contains("#")) {
            return name.substring(name.indexOf("#") + 1);
        } else {
            return name;
        }
    }

    private static String addPlural(String str) {
        if (str.endsWith("y")) {
            return str + "(ies)";
        } else if (str.endsWith("ss")) {
            return str + "(es)";
        } else {
            return str + "(s)";
        }
    }

    private static void convertMiddleTriple(Triple triple, int tripleNumber, OWLBasicModel2 model, NLText nlText) {
        nlText.addTextElement(new NLText.AnnotatedText(", which ", NLText.Format.PLAIN));
        convertPropertyAndDestination(triple, tripleNumber, model, nlText);
    }

    private static void convertPropertyAndDestination(Triple triple, int tripleNumber, OWLBasicModel2 model, NLText nlText) {
        nlText.addTextElement(new NLText.AnnotatedText(getLabel(triple.getRelation(), model), NLText.Format.RELATION, getRelationPosition(tripleNumber)));
        nlText.addTextElement(new NLText.AnnotatedText(" ", NLText.Format.PLAIN));
        nlText.addTextElement(new NLText.AnnotatedText(getLabel(triple.getDestination(), model), NLText.Format.CLASS, getDestinationPosition(tripleNumber)));
    }

    private static void addRestrictions(Path path, OWLBasicModel2 model, NLText nlText) {
        for (int i = 0; i < path.getClassNumber(); i++) {
            Restriction restriction = path.getRestrictions(i);
            if (restriction != null) {
                addRestriction(restriction, path.getClass(i), i, model, nlText);
            }
        }
    }

    private static void addRestriction(Restriction restriction, String aClass, int classPosition, OWLBasicModel2 model, NLText nlText) {
        nlText.addTextElement(new NLText.AnnotatedText(". The ", NLText.Format.PLAIN));
        nlText.addTextElement(new NLText.AnnotatedText(getLabel(aClass, model), NLText.Format.CLASS, classPosition));
        nlText.addTextElement(new NLText.AnnotatedText(" must be ", NLText.Format.PLAIN));
        nlText.addTextElement(new NLText.AnnotatedText(getOperatorText(restriction), NLText.Format.RESTRICTION_OPERATOR));
        nlText.addTextElement(new NLText.AnnotatedText(" ", NLText.Format.PLAIN));
        nlText.addTextElement(new NLText.AnnotatedText(restriction.getValue(), getOperandFormat(restriction)));
    }

    private static String getOperatorText(Restriction restriction) {
        switch (restriction.getOp()) {

            case EQUAL:
                return "equal to";
            case NOT_EQUAL:
                return "different from";
            case LESS:
                return "less than";
            case LESS_EQUAL:
                return "less or equal than";
            case GREATER:
                return "greater than";
            case GREATER_EQUAL:
                return "greater or equal than";
        }
        return null;
    }

    private static NLText.Format getOperandFormat(Restriction restriction) {
        if (restriction.getRange() == Restriction.Range.STRING || restriction.getRange() == Restriction.Range.INSTANCE) {
            return NLText.Format.RESTRICTION_OPERAND_TEXT;
        } else {
            return NLText.Format.RESTRICTION_OPERAND_NUMBER;
        }
    }

    private static int getOriginPosition(int tripleNumber) {
        return tripleNumber;
    }

    private static int getRelationPosition(int tripleNumber) {
        return getOriginPosition(tripleNumber) + 1;
    }

    private static int getDestinationPosition(int tripleNumber) {
        return getOriginPosition(tripleNumber) + 1;
    }
}
